package com.yzq.os.spider.v.service.job;

import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;

import com.yzq.os.spider.v.domain.SpiderTask;
import com.yzq.os.spider.v.service.domain.SpiderTaskService;
import com.yzq.os.spider.v.service.domain.ServerService;
import com.yzq.os.spider.v.util.IPUtils;

/**
 * 定时任务管理类
 * 
 * @author 苑志强(xingyu_yzq@163.com)
 * 
 */
@Service
public class SchedulerService {

	private static Logger logger = Logger.getLogger(SchedulerService.class);

	public static final String TASK_DATA_MAP_KEY = "task";
	/* job names */
	private static final String CLEANER_JOB_NAME = "job-cleaner";
	/* trigger names */
	private static final String CLEANER_TRIGGER_NAME = "trigger-cleaner";

	private static final String JOB_PREFIX = "CRAWL_JOB_";
	private static final String TRIGGER_PREFIX = "CRAWL_TRIGGER_";

	@Autowired
	private SpiderTaskService spiderTaskService;

	@Autowired
	private ServerService serverService;

	@Value("${app.cleanerCronExp}")
	private String cleanerCronExp;

	@Value("${app.excludeCleanEngineIds}")
	private String excludeCleanEngineIds;

	private static final Class<? extends QuartzJobBean> cleanerClass = CleanJobDetail.class;
	private static final Class<? extends QuartzJobBean> jobDetailClass = SpiderRecordDetail.class;

	private static Scheduler scheduler;

	static {
		try {
			scheduler = StdSchedulerFactory.getDefaultScheduler();
		} catch (SchedulerException e) {
			logger.error("", e);
		}
	}

	public void putLocalCrawlTask() throws SchedulerException {
		List<String> localIps = IPUtils.getLocalIPAddress();
		logger.info("Local ip address is:[" + ArrayUtils.toString(localIps)
				+ "].");
		List<SpiderTask> tasks = spiderTaskService.findLocalTasks(localIps);
		for (SpiderTask task : tasks) {
			try {
				reLoad(task);
			} catch (ParseException e) {
				logger.error("ParseException", e);
			}
		}
		logger.info("Load crawl task size:[" + CollectionUtils.size(tasks)
				+ "].");
	}

	public void putCleanQueryUrlTask() throws ParseException,
			SchedulerException {
		boolean isMaster = serverService.isLocalMaster();
		if (isMaster && StringUtils.isNotBlank(cleanerCronExp)) {
			JobDetail jobDetail = new JobDetail(CLEANER_JOB_NAME,
					Scheduler.DEFAULT_GROUP, cleanerClass);
			CronTrigger trigger = new CronTrigger(CLEANER_TRIGGER_NAME,
					Scheduler.DEFAULT_GROUP, cleanerCronExp);
			trigger.setJobName(CLEANER_JOB_NAME);
			trigger.setJobGroup(Scheduler.DEFAULT_GROUP);
			Map<String, String> map = new HashMap<String, String>();
			map.put(CleanJobDetail.EXCLUDE_CLEAN_ENGINEIDS_KEY,
					excludeCleanEngineIds);
			JobDataMap jobDataMap = new JobDataMap(map);
			jobDetail.setJobDataMap(jobDataMap);
			scheduler.scheduleJob(jobDetail, trigger);
			logger.info("Local is Master. put cleaner task to schedule. cronExp["
					+ cleanerCronExp + "]");
		}
	}

	private void reLoad(SpiderTask task) throws SchedulerException,
			ParseException {
		boolean isExist = isRunInScheduler(task);
		if (isExist) {
			remove(task);
		}
		put(task);
	}

	private void put(SpiderTask task) throws SchedulerException, ParseException {
		JobDetail jobDetail = createClassDetail(jobDetailClass, task);
		CronTrigger cronTrigger = createClassCronTrigger(jobDetailClass, task);
		scheduler.scheduleJob(jobDetail, cronTrigger);
		logger.info("Put task:[" + task + "] into scheduler.");
	}

	public void remove(SpiderTask task) throws SchedulerException {
		scheduler.deleteJob(makeJobName(task), Scheduler.DEFAULT_GROUP);
		logger.info("Delete task:[" + task + "] from scheduler.");
	}

	private boolean isRunInScheduler(SpiderTask task) throws SchedulerException {
		boolean returnValue = false;
		Trigger[] triggers = scheduler.getTriggersOfJob(makeJobName(task),
				Scheduler.DEFAULT_GROUP);
		if (triggers != null && triggers.length > 0) {
			for (Trigger trigger : triggers) {
				if (trigger.getName().equals(makeTriggerName(task))) {
					returnValue = true;
					break;
				}
			}
		}
		return returnValue;
	}

	private JobDetail createClassDetail(Class<? extends QuartzJobBean> clazz,
			SpiderTask task) {
		JobDetail jobDetail = new JobDetail(makeJobName(task),
				Scheduler.DEFAULT_GROUP, clazz);
		JobDataMap map = new JobDataMap();
		map.put(TASK_DATA_MAP_KEY, task);
		jobDetail.setJobDataMap(map);
		return jobDetail;
	}

	private CronTrigger createClassCronTrigger(
			Class<? extends QuartzJobBean> clazz, SpiderTask task)
			throws ParseException {
		CronTrigger trigger = new CronTrigger(makeTriggerName(task),
				Scheduler.DEFAULT_GROUP, task.getCronExp());
		trigger.setJobName(makeJobName(task));
		trigger.setJobGroup(Scheduler.DEFAULT_GROUP);
		return trigger;
	}

	private String makeJobName(SpiderTask task) {
		return JOB_PREFIX + task.getId();
	}

	private String makeTriggerName(SpiderTask task) {
		return TRIGGER_PREFIX + task.getId();
	}

	public void startScheduler() {
		try {
			scheduler.start();
		} catch (SchedulerException e) {
			logger.error("", e);
		}
		logger.info("SchedulerFactoryBean is started.");
	}

	public void shutdownScheduler() {
		try {
			scheduler.shutdown();
		} catch (SchedulerException e) {
			logger.error("", e);
		}
		logger.info("SchedulerFactoryBean is stoped.");
	}

}
